#include "flash_board.h"
#include "real_time_app.h"
#include "string.h"

// void FlashEraseBlock(uint32_t EraseStartAddr, uint32_t EraseSize)
// {
//   EraseStartAddr += FLASH_START_WRITE_ADDRESS;
//   FLASH_ROM_ERASE(EraseStartAddr, EraseSize);
// }

//  void FlashWriteBlockNoCheck(uint32_t WriteStartAddr, uint8_t *pDataIn)
// {
//   if (pDataIn != NULL)
//   {
//     // WriteStartAddr += FLASH_START_WRITE_ADDRESS;
//     PRINT("write rom");
//     FLASH_ROM_WRITE(WriteStartAddr, (uint32_t *)pDataIn, FLASH_MAX_SECTOR_SIZE);
//   }
// }

// void FlashReadCacheData(uint32_t ReadStartAddr, uint8_t *pDataOut, uint32_t SizeOut)
// {
//   if ((pDataOut != NULL) && (SizeOut != 0))
//   {
//     // FLASH_ROM_READ(ReadStartAddr, pDataOut, SizeOut);
//     ReadStartAddr += FLASH_START_WRITE_ADDRESS;
//     for (uint32_t ReadOffset = 0; ReadOffset < SizeOut; ReadOffset++)
//     {
//       pDataOut[ReadOffset] = (*(__IO uint8_t *)(ReadStartAddr + ReadOffset));
//     }
//   }
// }

/* Global define */
typedef enum {FAILED = 0, PASSED = !FAILED} TestStatus;
#define PAGE_WRITE_START_ADDR  ((uint32_t)0x08020000) /* Start from 32K */
#define PAGE_WRITE_END_ADDR    ((uint32_t)0x08021000) /* End at 36K */
#define FLASH_PAGE_SIZE                   4096
#define FLASH_PAGES_TO_BE_PROTECTED FLASH_WRProt_Pages60to63

/* Fast Mode define */
// #define FAST_FLASH_PROGRAM_START_ADDR  ((uint32_t)0x08008000)
// #define FAST_FLASH_PROGRAM_END_ADDR  ((uint32_t)0x08010000)
// #define FAST_FLASH_SIZE  (64*1024)

/* Global Variable */
uint32_t EraseCounter = 0x0, Address = 0x0;
uint16_t Data = 0xAAAA;
uint32_t WRPR_Value = 0xFFFFFFFF, ProtectedPages = 0x0;
uint32_t NbrOfPage;
volatile FLASH_Status FLASHStatus = FLASH_COMPLETE;
volatile TestStatus MemoryProgramStatus = PASSED;
volatile TestStatus MemoryEraseStatus = PASSED;

#define Fadr    (0x08020000)
#define Fsize   ((((256*4))>>2))
u32 buf[Fsize];

/*********************************************************************
 * @fn      Flash_Test
 *
 * @brief   Flash Program Test.
 *
 * @return  none
 */
void Flash_Test(void)
{
    printf("FLASH Test\n");

    FLASH_Unlock();

    NbrOfPage = (PAGE_WRITE_END_ADDR - PAGE_WRITE_START_ADDR) / FLASH_PAGE_SIZE;

    FLASH_ClearFlag(FLASH_FLAG_BSY | FLASH_FLAG_EOP |FLASH_FLAG_WRPRTERR);

    for(EraseCounter = 0; (EraseCounter < NbrOfPage) && (FLASHStatus == FLASH_COMPLETE); EraseCounter++)
    {
      FLASHStatus = FLASH_ErasePage(PAGE_WRITE_START_ADDR + (FLASH_PAGE_SIZE * EraseCounter));  //Erase 4KB

      if(FLASHStatus != FLASH_COMPLETE)
      {
        printf("FLASH Erase Fail\r\n");
        return;
      }
      printf("FLASH Erase Suc\r\n");
    }

    Address = PAGE_WRITE_START_ADDR;
    printf("Programing...\r\n");
    while((Address < PAGE_WRITE_END_ADDR) && (FLASHStatus == FLASH_COMPLETE))
    {
      FLASHStatus = FLASH_ProgramHalfWord(Address, Data);
      Address = Address + 2;
    }

    Address = PAGE_WRITE_START_ADDR;

    printf("Program Cheking...\r\n");
    while((Address < PAGE_WRITE_END_ADDR) && (MemoryProgramStatus != FAILED))
    {
      PRINT("Data %08x ,Address %8x \r\n",Data,*(__IO uint16_t*) Address);
      if((*(__IO uint16_t*) Address) != Data)
      {
        MemoryProgramStatus = FAILED;
      }
      Address += 2;
    }

    if(MemoryProgramStatus == FAILED)
    {
       printf("Memory Program FAIL!\r\n");
    }
    else
    {
       printf("Memory Program PASS!\r\n");
    }

    FLASH_Lock();
}

/*********************************************************************
 * @fn      Flash_Test_Fast
 *
 * @brief   Flash Fast Program Test.
 *
 * @return  none
 */
void Flash_Test_Fast(void)
{
    u32 i;
    u8 Verify_Flag = 0;
    u8 Readdate[4096];
    FLASH_Status s;
    PRINT("Fsize %08x \r\n",Fsize);
    for(i = 0; i < Fsize; i++){
        buf[i] = i;
    }

    printf("Read flash\r\n");
    // for(i=0; i<Fsize; i++){
    //     printf("adr-%08x v-%08x\r\n", Fadr +4*i, *(u32*)(Fadr +4*i));
    // }
    FlashReadCacheData(0,Readdate,256);
    for(i=0; i<256; i++){
        printf("adr-%08x\r\n", Readdate[i]);
    }
    // s = FLASH_ROM_ERASE(Fadr, Fsize*4);
    // if(s != FLASH_COMPLETE)
    // {
    //     printf("check FLASH_ADR_RANGE_ERROR FLASH_ALIGN_ERROR or FLASH_OP_RANGE_ERROR\r\n");
    //     return;
    // }
    FlashEraseBlock(0,256*4);
    printf("Erase flash\r\n");
     for(i=0; i<256; i++){
        printf(" v-%08x\r\n\r\n", Readdate[i]);
    }

    // s = FLASH_ROM_WRITE(Fadr,  buf, Fsize*4);
    // if(s != FLASH_COMPLETE)
    // {
    //     printf("check FLASH_ADR_RANGE_ERROR FLASH_ALIGN_ERROR or FLASH_OP_RANGE_ERROR\r\n");
    //     return;
    // }
    FlashWriteBlockNoCheck(0,(uint8_t *)buf);
    printf("Write flash\r\n");
    // for(i=0; i<Fsize; i++){
    //     printf("adr-%08x v-%08x\r\n", Fadr +4*i, *(u32*)(Fadr +4*i));
    // }
    FlashReadCacheData(0,Readdate,256);
    for(i=0; i<256; i++){
        printf(" v-%08x\r\n\r\n",Readdate[i]);
    }

    for(i = 0; i < Fsize; i++){
        if(buf[i] == *(u32 *)(Fadr + 4 * i))
        {
            Verify_Flag = 0;
        }
        else
        {
            Verify_Flag = 1;
            break;
        }
    }

    if(Verify_Flag)
        printf("%d Byte Verify Fail\r\n", (Fsize*4));
    else
        printf("%d Byte Verify Suc\r\n", (Fsize*4));
}



uint8_t FlashEraseBlock(uint32_t EraseStartAddr, uint32_t EraseSize)
{
  FLASH_Status s;
  EraseStartAddr += 0x8000000;//FLASH_START_WRITE_ADDRESS;
  s=FLASH_ROM_ERASE(EraseStartAddr, EraseSize);
  PRINT("Erase rom EraseStartAddr %0x  s %02x \r\n",EraseStartAddr,s);
  return s;
}

uint8_t FlashWriteBlockNoCheck(uint32_t WriteStartAddr, uint8_t *pDataIn)
{
    FLASH_Status s;
    uint32_t WriteDate[1024];
    uint16_t i=0;
    PRINT("write date\r\n");
    for(i=0;i<374;i++)
      PRINT("%02x ",pDataIn[i]);
    if (pDataIn != NULL)
    {
      WriteStartAddr += 0x8000000;//FLASH_START_WRITE_ADDRESS;
      PRINT("write rom WriteStartAddr %0x\r\n",WriteStartAddr);
      for(i=0;i<1024;i++)
      {
        WriteDate[i]=pDataIn[4*i]<<24|pDataIn[4*i+1]<<16|pDataIn[4*i+2]<<8|pDataIn[4*i+3];
        // PRINT("pDataIn %02x %02x %02x %02x \r\n",pDataIn[4*i],pDataIn[4*i+1],pDataIn[4*i+2],pDataIn[4*i+3]);
        // PRINT("WriteDate %08x \r\n",WriteDate[i]);
      }
      s=FLASH_ROM_WRITE(WriteStartAddr, (uint32_t *)WriteDate, 1024);
      PRINT("s %02x \r\n",s);
    }
    return s;
}

void FlashReadCacheData(uint32_t ReadStartAddr, uint8_t *pDataOut, uint32_t SizeOut)
{
  uint32_t Readdate[SizeOut/4];
  uint16_t i=0;
  if ((pDataOut != NULL) && (SizeOut != 0))
  {
    // FLASH_ROM_READ(ReadStartAddr, pDataOut, SizeOut);
    ReadStartAddr += 0x8000000;//FLASH_START_WRITE_ADDRESS;
    PRINT("ReadStartAddr %0x SizeOut %0d \r\n",ReadStartAddr,SizeOut);
    for (uint32_t ReadOffset = 0; ReadOffset <( SizeOut/4); ReadOffset++)
    {
      Readdate[ReadOffset]=*(u32*)(ReadStartAddr + ReadOffset*4);
      pDataOut[ReadOffset*4]=Readdate[ReadOffset]>>24;
      pDataOut[ReadOffset*4+1]=Readdate[ReadOffset]>>16;
      pDataOut[ReadOffset*4+2]=Readdate[ReadOffset]>>8;
      pDataOut[ReadOffset*4+3]=Readdate[ReadOffset];
      // PRINT("Readdate %8x \r\n",Readdate[ReadOffset]);
      // pDataOut[ReadOffset] = (*(__IO uint8_t *)(ReadStartAddr + ReadOffset*4));
      // PRINT("pDataOut %02x ",pDataOut[ReadOffset]);
    }
  }
}


bool FlashWriteCacheDataToCheak(uint32_t WriteStartAddr, const void *pDataIn, uint32_t SizeIn)
{
  bool WriteDataResult = false;
  uint16_t i=0;
  uint8_t NumberOfMatchingFailures = 0, *pSectorDataBuff = NULL;
  uint32_t SectorAddressOffset = 0, SectorMemoryOffset = 0, SectorBuffOffset = 0;
  PRINT("[info] : WriteStartAddr %02x,SizeIn = %d\r\n", WriteStartAddr, SizeIn);
  if (pDataIn != NULL)
  {
    if ((SizeIn > 0) && ((WriteStartAddr + SizeIn) < FLASH_MAX_OPERATION_LENGTH))
    {
      pSectorDataBuff = tmos_msg_allocate(FLASH_MAX_SECTOR_SIZE);
      if (pSectorDataBuff != NULL)
      {
        while (1)
        {
          SectorAddressOffset = WriteStartAddr / FLASH_MAX_SECTOR_SIZE;
          SectorMemoryOffset = WriteStartAddr % FLASH_MAX_SECTOR_SIZE;
          SectorBuffOffset = FLASH_MAX_SECTOR_SIZE - SectorMemoryOffset;

          if (SectorBuffOffset > SizeIn)
          {
            SectorBuffOffset = SizeIn;
          }
          // PRINT("writing\r\n");
          FlashReadCacheData(SectorAddressOffset * FLASH_MAX_SECTOR_SIZE, pSectorDataBuff, FLASH_MAX_SECTOR_SIZE);
          for (uint32_t i = 0; i < FLASH_MAX_SECTOR_SIZE; i++)
          {
            if (pSectorDataBuff[i] != 0xFF)
            {
              // PRINT("earse ing\r\n");
              FlashEraseBlock(SectorAddressOffset * FLASH_MAX_SECTOR_SIZE, FLASH_MAX_SECTOR_SIZE);
              break;
            }
          }

          memcpy(&pSectorDataBuff[SectorMemoryOffset], pDataIn, SectorBuffOffset);
          // PRINT("WRITE date:");
          // for(i=0;i<4096;i++)
          // {
          //   PRINT("%02x ",pSectorDataBuff[i]);
          // }
          // PRINT("\r\n");
          FlashWriteBlockNoCheck(SectorAddressOffset * FLASH_MAX_SECTOR_SIZE,  pSectorDataBuff);
          // PRINT("write over\r\n");
          FlashReadCacheData(SectorAddressOffset * FLASH_MAX_SECTOR_SIZE, pSectorDataBuff, FLASH_MAX_SECTOR_SIZE);
          // PRINT("read date:");
          // for(i=0;i<4096;i++)
          // {
          //   PRINT("%02x ",pSectorDataBuff[i]);
          // }
          PRINT("\r\n");
          if (memcmp(&pSectorDataBuff[SectorMemoryOffset], pDataIn, SectorBuffOffset) == 0)
          {
            if (SectorBuffOffset == SizeIn)
            {
              WriteDataResult = true;
              break;
            }

            WriteStartAddr += SectorBuffOffset;
            pDataIn += SectorBuffOffset;
            SizeIn -= SectorBuffOffset;
          }
          else
          {
            NumberOfMatchingFailures++;
            if (NumberOfMatchingFailures > FLASH_MAX_REWRITE_NUMBER)
            {
                PRINT("write fail\r\n");
                break;
            }
              
          }
        }

        tmos_msg_deallocate(pSectorDataBuff);
      }
      else
      {
        WriteDataResult = false;
        PRINT("[flash]: there is not enough space. \r\n");
      }
    }
  }

  return WriteDataResult;
}

uint8_t FlashCalculateFileAccumulateCleckCode(uint32_t AddressOffset, uint32_t FilzeSize)
{
  uint8_t *pSectorDataBuff = NULL;
  uint32_t CheckCode = 0, GetOffset = 0, GetSize = 0;

  pSectorDataBuff = tmos_msg_allocate(FLASH_MAX_SECTOR_SIZE);
  if (pSectorDataBuff != NULL)
  {
    if ((FilzeSize > 0) && ((AddressOffset + FilzeSize) < FLASH_MAX_OPERATION_LENGTH))
    {
      while (FilzeSize > 0)
      {
        GetSize = (FilzeSize > FLASH_MAX_SECTOR_SIZE) ? FLASH_MAX_SECTOR_SIZE : FilzeSize;

        FlashReadCacheData(AddressOffset + GetOffset, pSectorDataBuff, GetSize);
        for (uint32_t i = 0; i < GetSize; i++)
        {
          CheckCode += pSectorDataBuff[i];
        }

        GetOffset += GetSize;
        FilzeSize -= GetSize;
      }
    }

    tmos_msg_deallocate(pSectorDataBuff);
  }

  return (uint8_t)(CheckCode & 0x000000FF);
}